﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MagicCove
{
    //General properties of a magic card, extended by individual classes <-- this needs to be done eventually currently all the card types are together
    //this allows easier morphism (take gideon), but is inefficient so it will need to be changed (gideon sE can create another card to replace itself or something)
    class MagicCard : MagicStackItem
    {
        private String cardName;
        private String specialText = "";//extra text to be displayed next to the card (for example ability going on stack). Note tapped will be handled by the game form, along with any other variables explecitely defined for a magic card.
        private Player owner; //specifies who owns the card
        /* Card Type int definitions:
         * 0-Artifact
         * 1-Creature
         * 2-Enchantment
         * 3-Instant
         * 4-Land
         * 5-Planeswalker
         * 6-Sorcery
         * 7-Tribal */
        private CardType cardTypes;
        private CardSupertype cardSupertypes;
        private CardSubtype cardSubtypes;
        private Color cardColor;
        private int attack; //this holds the attack of the card
        private int defense; //this holds the defense of a card without any combat damage taken into account
        private int tempAttack;//attack for specified turn, this is reset during the cleanup step
        private int tempDefense;//defense for specified turn, this is defense-combat damage this turn
        private CurrentLocation location = CurrentLocation.Deck; //the current location of the card (0-deck, 1-hand, 2-stack, 3-field, 4-graveyard, 5-exiled)
        private CurrentLocation locationGoing = CurrentLocation.Deck; //where the card is heading ^^
        private MagicField parentField; //the field the card is currently on. This may not match the owner
        private List<sEBase> sEAbilities;//special effect class, these are activated abilities
        private List<sEBase> sETriggered;//these are triggered abilities
        private iPBase iPC;//isPlayable class for playing magic card
        private bool countered;
        private int plusOneCounters = 0; //the number of plus one counters this card has on it. please note that the increase in Attack and Defense has already taken place
        private int minusOneCounters = 0; //the number of minus one counters this card has on it. please note that the reduction in Attack and Defense has already taken place
        private List<Keyword> keywords;

        public bool summoningSickness;//set to true when played, otherwise false
        public bool tapped = false;//specifies if the card is tapped or not
        public List<MagicCard> targets; //a list of magic cards the card targets/atached to
        public MagicCard combatTarget; //the card a creature targets during combat (defending or attacking)

        #region Constructor
        //assumes cardColor is just from mana types, later a second constructer need to be made to allow cardColor to be passed ~!~
        public MagicCard(String cardName, Player owner, CardSupertype cardSupertypes,  CardType cardTypes, CardSubtype cardSubtypes, int attack, int defense, iPBase iPC, List<sEBase> sEAbilities, List<sEBase> sETriggered)
        {
            this.cardName = cardName;
            this.owner = owner;
            this.attack = attack;
            tempAttack = attack;
            this.defense = defense;
            tempDefense = defense;
            this.cardTypes = cardTypes;
            this.cardSupertypes = cardSupertypes;
            this.cardSubtypes = cardSubtypes;
            this.keywords = new List<Keyword>();
            this.sEAbilities = sEAbilities;
            foreach (sEBase sE in this.sEAbilities)
            {
                sE.ParentCard = this;
                sE.IPClass.ParentCard = this;
            }
            this.sETriggered = sETriggered;
            foreach (sEBase sE in this.sETriggered)
            {
                sE.ParentCard = this;
                sE.IPClass.ParentCard = this;
            }
            this.iPC = iPC;
            this.iPC.ParentCard = this;
            if ((cardTypes & CardType.Creature) != 0)
                this.summoningSickness = true;
            else
                this.summoningSickness = false;
            this.targets = new List<MagicCard>();
            this.combatTarget = null;
            cardColor = Color.Colorless;
            if (this.iPC.ManaCost != null)
                foreach (ManaType mT in this.iPC.ManaCost)
                    cardColor = cardColor | (Color)mT;
        }
        #endregion
        #region Get Variables
        public String CardName
        {
            get
            {
                return this.cardName;
            }
        }
        public String ItemName
        {
            get
            {
                return this.cardName;
            }
        }
        public String SpecialText
        {
            get
            {
                return this.specialText;
            }
            set
            {
                this.specialText = value;
            }
        }
        public Player Owner
        {
            get
            {
                return this.owner;
            }
        }
        public MagicField ParentField
        {
            get
            {
                return this.parentField;
            }
            set
            {
                this.parentField = value;
            }
        }
        public CurrentLocation Location
        {
            get
            {
                return this.location;
            }
            set
            {
                this.location = value;
            }
        }
        public CurrentLocation LocationGoing
        {
            get
            {
                return this.locationGoing;
            }
            set
            {
                this.locationGoing = value;
            }
        }
        public List<TargetLocation> TargetLocations
        {//returns the cards target's location relative to this card (as TargetLocations)
            get
            {
                List<TargetLocation> retVal = new List<TargetLocation>();
                foreach (MagicCard mC in targets)
                {
                    if (mC.Owner == owner) // have same owners
                        switch (mC.Location)
                        {
                            case CurrentLocation.Deck: //cannot target anything in the deck
                                goto default;
                            case CurrentLocation.Hand:
                                retVal.Add(TargetLocation.CurrentPlayersHand);
                                break;
                            case CurrentLocation.Stack:
                                retVal.Add(TargetLocation.TheStack);
                                break;
                            case CurrentLocation.Field:
                                retVal.Add(TargetLocation.CurrentPlayersField);
                                break;
                            case CurrentLocation.Graveyard:
                                retVal.Add(TargetLocation.CurrentPlayersGraveyard);
                                break;
                            case CurrentLocation.Exiled:
                                retVal.Add(TargetLocation.CurrentPlayersExiled);
                                break;
                            default:
                                throw new System.Exception("Error in target loc conversion, current location not recognized");
                        }
                    else
                        switch (mC.Location)
                        {
                            case CurrentLocation.Deck:
                                goto default;
                            case CurrentLocation.Hand:
                                retVal.Add(TargetLocation.OpponentsHand);
                                break;
                            case CurrentLocation.Stack:
                                retVal.Add(TargetLocation.TheStack);
                                break;
                            case CurrentLocation.Field:
                                retVal.Add(TargetLocation.OpponentsField);
                                break;
                            case CurrentLocation.Graveyard:
                                retVal.Add(TargetLocation.OpponentsGraveyard);
                                break;
                            case CurrentLocation.Exiled:
                                retVal.Add(TargetLocation.OpponentsExiled);
                                break;
                            default:
                                throw new System.Exception("Error in target loc conversion, current location not recognized");
                        }
                }
                return retVal;
            }
        }
        public List<ManaType> ManaCost
        {
            get
            {
                return this.iPC.ManaCost;
            }
        }
        public int ConvertedManaCost
        {
            get
            {
                int cMC = 0;
                foreach (ManaType mT in iPC.ManaCost)
                {
                    if ((mT & ManaType.TwoColorless) != 0)
                        cMC += 2;
                    else if ((mT & ManaType.ThreeColorless) != 0)
                        cMC += 3;
                    else
                        cMC++;
                }
                return cMC;
            }
        }
        public List<sEBase> SEAbilities
        {
            get
            {
                return this.sEAbilities;
            }
        }
        public bool IsPlayable
        {
            get
            {//assumes that if it is playable it will be played.
                int[] tempFieldMana = (int[])parentField.Mana.Clone();
                int[] manaLeft = iPC.getManaLeft();
                foreach (int element in manaLeft)
                    if (element < 0)
                        return false;
                if (!iPC.isPlayable())
                    return false;
                for (int i = 0; i < 6; i++)//if the field has changed we must rebalance. (a sE added mana is the reason for this)
                {
                    parentField.Mana[i] = parentField.Mana[i] - tempFieldMana[i] + manaLeft[i];
                }
                return true;
            }
        }
        public iPBase IPClass
        {
            get
            {
                return this.iPC;
            }
        }
        public bool Countered
        {
            get
            {
                return this.countered;
            }
            set
            {
                this.countered = value;
            }
        }
        public CardType CardTypes
        {
            get
            {
                return this.cardTypes;
            }
            set
            {
                this.cardTypes = value;
            }
        }
        public CardSupertype CardSupertypes
        {
            get
            {
                return this.cardSupertypes;
            }
            /*set
            {
                this.cardSupertypes = value;
            }*/
        }
        public CardSubtype CardSubtypes
        {
            get
            {
                return this.cardSubtypes;
            }
            /*set
            {
                this.cardSubtypes = value;
            }*/
        }
        public Color CardColor
        {
            get
            {
                return this.cardColor;
            }
            set
            {
                this.cardColor = value;
            }
        }
        public List<Keyword> Keywords
        {
            get
            {
                return this.keywords;
            }
        }
        public int Attack
        {
            get
            {
                return this.attack + plusOneCounters - minusOneCounters;
            }
            set
            {
                this.attack = value - plusOneCounters + minusOneCounters; //so that Attack++ works properly
            }
        }
        public int TempAttack
        {
            get
            {
                return this.tempAttack + plusOneCounters - minusOneCounters;
            }
            set
            {
                this.tempAttack = value - plusOneCounters + minusOneCounters;
            }
        }
        public int Defense
        {
            get
            {
                return this.defense + plusOneCounters - minusOneCounters;
            }
            set
            {
                this.defense = value - plusOneCounters + minusOneCounters;
            }
        }
        public int TempDefense
        {
            get
            {
                return this.tempDefense + plusOneCounters - minusOneCounters;
            }
            set
            {
                this.tempDefense = value - plusOneCounters + minusOneCounters;
            }
        }
        public int PlusOneCounters
        {
            get
            {
                return this.plusOneCounters;
            }
            set
            {
                this.plusOneCounters = value;
            }
        }
        public int MinusOneCounters
        {
            get
            {
                return this.minusOneCounters;
            }
            set
            {
                this.minusOneCounters = value;
            }
        }
        #endregion
        //Other Methods
        public void sE ()
        {
            foreach (sEBase specialEffectElement in sETriggered)
            {
                if (specialEffectElement.IsPlayable)
                    specialEffectElement.specialEffect();
            }
        }
        public void popedFromStack()
        {
            sE(); //so that it activates it sE first
        }
    }
}